---
order: 7.4
category: '@threlte/extras'
sourcePath: 'packages/extras/src/lib/components/InstancedSprite/InstancedSprite.svelte'
title: <InstancedSprite>
type: 'component'
componentSignature:
  {
    props:
      [
        {
          name: 'alphaTest',
          type: 'number',
          required: false,
          default: '0.1',
          description: 'Sets the alpha value to be used when running an alpha test.'
        },
        {
          name: 'autoUpdate',
          type: 'boolean',
          required: false,
          default: 'true',
          description: 'Update animations automatically every frame'
        },
        {
          name: 'baseMaterial',
          type: 'typeof MeshBasicMaterial | typeof MeshStandardMaterial | typeof MeshLambertMaterial | typeof MeshPhongMaterial',
          required: false,
          default: 'typeof MeshBasicMaterial',
          description: 'Base material used to construct the sprite material.'
        },
        {
          name: 'billboarding',
          type: 'boolean',
          required: false,
          default: 'typeof MeshBasicMaterial',
          description: 'Sets the default global billboarding state that is used unless the setAt was called on the instance.'
        },
        { name: 'count', type: 'number', required: true, description: 'Number of instances' },
        {
          name: 'fps',
          type: 'boolean',
          required: false,
          default: '10',
          description: 'The desired frames per second of the animation.'
        },
        {
          name: 'playmode',
          type: '"FORWARD" | "REVERSE" | "PAUSE" | "PINGPONG"',
          required: false,
          default: '"FORWARD"',
          description: 'Sets playmode for all instances'
        },
        {
          name: 'transparent',
          type: 'boolean',
          required: false,
          default: 'true',
          description: 'Whether or not the material should be transparent.'
        },
        {
          name: 'randomPlaybackOffset',
          type: 'boolean | number',
          required: false,
          default: 'true',
          description: 'Offset sprite animation time by a random number of milliseconds.'
        }
      ],
    bindings: [{ name: ref, type: 'InstancedSpriteMesh' }]
  }
---

import ComponentSignature from '$components/ComponentSignature/ComponentSignature.astro'

<Tip type="experimental">
  This is an early version - features and API might change significantly over time. Please report
  any issues you encounter on Discord or Github.
</Tip>

The `<InstancedSprite>` component allows you to efficiently spawn large numbers of animated
[sprites](<https://en.wikipedia.org/wiki/Sprite_(computer_graphics)>) in your scene and update each instance
using the `useInstancedSprite` hook, or with a `<Instance>` component available through a slot prop.

You can find an example of a [more complex scene here](/docs/examples/animation/complex-sprite-scene).

<Example path="extras/instanced-sprite" />

## `<InstancedSprite>`

To use the `<InstancedSprite>` you must provide it with sprite metadata and a texture. While we recommend utilizing the
[`buildSpritesheet()`](#buildSpritesheet) utility for this purpose, you are also free to implement your own
custom solution, provided it meets the component's input requirements.

Other than it's own props, `<InstanedSprite/>` extends and accepts all properties of
[Three.js instanced mesh](https://threejs.org/docs/#api/en/objects/InstancedMesh), such as `castShadow`, `frustumCulled` etc.

```svelte
<InstancedSprite
  bind:ref
  {spritesheet}
  count={500}
  playmode={'FORWARD'}
  fps={9}
  billboarding
  hueShift={{ h: 1.5, s: 0.9, v: 1 }}
  randomPlaybackOffset={2000}
  castShadow
/>
```

#### Required props

| Prop          | description                                                                                             |
| ------------- | ------------------------------------------------------------------------------------------------------- |
| `count`       | number of instances                                                                                     |
| `spritesheet` | Object with `spritesheet` metadata and a `texture` `{spritesheet: SpritesheetFormat, texture: Texture}` |

#### Optional props

| Prop                   | description                                                                                                                                                                                                                                                                     |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `autoUpdate`           | Update animations automatically. It should stay `true` for most usecases. Setting to `false` is most commonly used in case of [static sprites](#static-sprites--atlassing) but it can also be used for advanced manual animation updates.                                       |
| `billboarding`         | Sets the default global billboarding (sprites always facing the camera) state that is used unless the setAt was called on the instance.                                                                                                                                         |
| `playmode`             | Sets playmode for all instances. `"FORWARD" \| "REVERSE" \| "PAUSE" \| "PINGPONG"`                                                                                                                                                                                              |
| `fps`                  | The desired frames per second of the animation                                                                                                                                                                                                                                  |
| `alphaTest`            | Sets the alpha value to be used when running an alpha test                                                                                                                                                                                                                      |
| `transparent`          | Whether or not the material should be transparent                                                                                                                                                                                                                               |
| `randomPlaybackOffset` | Offset each sprite's animation timer by a random number of milliseconds. If `true`, randomness is within 0-100ms range. Providing the prop with a number sets the upper range of the offset - `randomPlaybackOffset={2000}` means that the animation will be offset by 0-2000ms |
| `hueShift`             | Changes sprite look by tweaking the material's output color by a provided hueShift, saturation and vibrance `{h: number, s: number, v:number}`                                                                                                                                  |

You create and update instances in three ways:

1. Utilizing the `useInstancedSprite()` hook (recommended approach).
2. Using the `<Instance>` component offered through a slot prop.
3. Directly with the underlying class using the `ref` binding.

### `useInstancedSprite`

The hook has to be used in a child component of `<InstancedSprite>` and returns an object with following properties:

- `count`: Total number of instances.
- `updatePosition(id: number, position: Vector3Tuple, scale?: Vector2Tuple)`: A utility function for updating an instance's position and scale.
- `animationMap`: A writable store (`Writable<Map<string, number>>`) that maps animation names to their corresponding IDs.
  Animation names are useful to have for setting a random animation from a pool etc. The IDs are reserved for more advanced usecases.
- `sprite`: Provides direct access to the `InstancedSpriteMesh`, enabling updates to instance properties such as animations, billboarding, and play mode.

```ts
import { useInstancedSprite } from '@threlte/extras'

const hook = useInstancedSprite()
// it's useful to immediately destructure it like this
const { updatePosition, count, animationMap, sprite } = useInstancedSprite()

// Examples of using the InstancedSpriteMesh API:

// play animation on instance id 0 - loops by defualt
sprite.play('IdleBackward').at(0)
// play animation without looping
sprite.play('RunLeft', false).at(1)
// play animation backwards with looping
sprite.play('RunLeft', true, 'REVERSE').at(2)

// mesh.play is a utility that combines the use of these functions:
// animation by name
sprite.animation.setAt(0, 'RunBackward')
// looping y/n
sprite.loop.setAt(0, false)
// animation direction - FORWARD (default) / REVERSE / PAUSE
sprite.playmode.setAt(0, 'REVERSE')

// billboarding
sprite.billboarding.setAll(true)
sprite.billboarding.setAt(0, true)
```

#### Typescript support

The useInstancedSprite hook supports typing for autocompletion of animation names:

```ts
type AnimationNames = 'walk' | 'run' | 'idle' | 'fly'
const { updatePosition, count, animationMap, sprite } = useInstancedSprite<AnimationNames>()
```

### `<Instance>`

Instance is a slot prop component that is used to update sprite instances properties. You can gain access to it with the `Instance` snippet prop on `InstancedSprite` component.
Then put it as a child component. The only required property is `id`. It also has `position` of type `Vector3Tuple` prop and `scale` of type `Vector2Tuple`.

Other than this, it as other properties that you can find in the `InstancedSpriteMesh`, so: `animationName`, `playmode`, `billboarding`, `offset`, `loop`,
`flipX`, `flipY`, `frameId`. Read more about them in the [InstancedSpriteMesh](#instancedspritemesh) section

The `<Instance>` component serves as a declarative alternative to `useInstancedSprite` hook to dynamically update the properties of sprite instances within the
`InstancedSprite` component. You can access through the `Instance` snippet prop.

_Example: Set a position, scale and flipX for every instance._

```svelte
<InstancedSprite
  count={10000}
  {spritesheet}
>
  {#snippet children({ Instance })}
    {#each { length: 10000 } as _, i}
      <Instance
        position={[Math.random() * 100, Math.random() * 100, Math.random() * 100]}
        scale={[3, 3]}
        flipX
        id={i}
      />
    {/each}
  {/snippet}
</InstancedSprite>
```

<ComponentSignature
  titleOverride={` `}
  idOverride="instance-component-signature"
  componentName={'<Instance/>'}
  signature={{
    props: [
      {
        name: 'id',
        type: 'number',
        required: true,
        description: 'Instance id. Ranges between 0 and the `count` value of InstacedSpriteMesh.'
      },
      {
        name: 'animationName',
        type: 'string',
        required: false,
        description: 'Sets an active animation by name.'
      },
      {
        name: 'position',
        type: 'Vector3Tuple',
        required: false,
        description: 'Sets the position of an instance.'
      },
      {
        name: 'scale',
        type: 'Vector2Tuple',
        required: false,
        description: 'Sets the scale of an instance.'
      },
      {
        name: 'playmode',
        type: '"FORWARD" | "REVERSE" | "PAUSE" | "PINGPONG"',
        required: false,
        description: 'Sets the playmode of an instance.'
      },
      {
        name: 'billboarding',
        type: 'boolean',
        required: false,
        description: 'Toggles billboarding on/off.'
      },
      {
        name: 'offset',
        type: 'number',
        required: false,
        description: 'Sets the exact time value in ms by which the animation playback is offset.'
      },
      {
        name: 'loop',
        type: 'boolean',
        required: false,
        description:
          'Toggles looping of the animation on/off. If off, the last played frame is displayed when the animation completes.'
      },
      {
        name: 'flipX',
        type: 'boolean',
        required: false,
        description: 'Toggles flipping the sprite horizontally.'
      },
      {
        name: 'flipY',
        type: 'boolean',
        required: false,
        description: 'Toggles flipping the sprite vertically.'
      },
      {
        name: 'frameId',
        type: 'number',
        required: false,
        description: `Sets an exact frame to display. If animationName is set, it's the id of the frame in that given animation, otherwise it's a spritesheet frame id.`
      }
    ]
  }}
/>

### binding

The `InstancedSpriteMesh` class is the foundation behind the `<InstancedSprite>` component, written to enable efficient instancing of animated sprites within a
Three.js environment. The `<InstancedSprite>` component lets you bind to it through `ref`.

The class extends the capabilities of the [troika's InstancedUniformsMesh](https://protectwise.github.io/troika/three-instanced-uniforms-mesh/). For an in-depth exploration of `InstancedSpriteMesh` and its features, refer to the documentation available at [InstancedSpriteMesh docs](https://three-kit.vercel.app/instancedsprite/01-instanced-sprite-mesh/).

## Spritesheets

### SpritesheetMetadata

Object used in `buildSpritesheet` function has to be compliant with the `SpritesheetMetadata` type format. This type is structured to
accommodate the metadata for one or multiple sprite files within a single spritesheet.

```ts
type SpritesheetMetadata = {
  url: string
  type: 'rowColumn' | 'frameSize'
  width: number
  height: number
  animations: {
    name: string
    frameRange: [number, number]
  }[]
}[]
```

#### Understanding SpritesheetMetadata

A `SpritesheetMetadata` is an array, with each entry representing metadata fields for one sprite:

    - `url`: Specifies the path or URL to the sprite image file.
    - `type`: Determines the method of defining the spritesheet dimensions. Type `"rowColumn"` specifies the layout in terms of rows
    and columns within the image, and type `"frameSize"` instead defines the size of each frame, allowing the utility to calculate the layout.
    - `width` and `height`: Depending on type, these refer to the number of `columns` and `rows` (`"rowColumn"`) or the dimensions of a single frame (`"frameSize"`).
    - `animations`: An array detailing the animations, where each animation has a name and a `frameRange`. The `frameRange` is a tuple
    marking the start and end frames of the animation.

#### Typesafety

For improved developer experience when working with TypeScript, it is strongly recommended to use `as const` assertion in
combination with `satisfies SpritesheetMetadata`. This approach not only ensures compliance with the `SpritesheetMetadata`
type but also enables autocompletion for animation names within utility functions, which is highly recommended:

### `buildSpritesheet()`

`buildSpritesheet()` is a utility function for building a final texture and spritesheet object from a provided `SpritesheetMetadata` object or external source.
Each `buildSpritesheet` method return an Promise that and has to be awaited. Promise returned by each method contains an object with a `spritesheet`
ready for use in `<InstancedSprite>`.

#### buildSpritesheet().from(meta: SpritesheetMetadata)

Other than `spritesheet` promise, it also returns a `useInstancedSprite` hook. This hook can be enhanced with
extra typescript support for autocompletion of animation names as such:

```ts
const meta = [
  {
    url: '/textures/sprites/cacodaemon.png',
    type: 'rowColumn',
    width: 8,
    height: 4,
    animations: [
      { name: 'fly', frameRange: [0, 5] },
      { name: 'attack', frameRange: [8, 13] },
      { name: 'idle', frameRange: [16, 19] },
      { name: 'death', frameRange: [24, 31] }
    ]
  }
] as const satisfies SpritesheetMetadata

const result = buildSpritesheet.from<typeof meta>(meta)
```

![Tree sprite atlas](/images/docs/extras/instanced-sprite/useInstancedSpriteAutocomplete.png)

#### buildSpritesheet().fromAseprite(asepriteDataUrl: string, spriteImageUrl: string)

Similar to above, but it parses the Aseprite metadata json into the correct format.
Does not provide any additional utilities.

### Examples

#### Multiple animations

```ts
const demonSpriteMeta = [
  {
    url: '/textures/sprites/cacodaemon.png',
    type: 'rowColumn',
    width: 8,
    height: 4,
    animations: [
      { name: 'fly', frameRange: [0, 5] },
      { name: 'attack', frameRange: [8, 13] },
      { name: 'idle', frameRange: [16, 19] },
      { name: 'death', frameRange: [24, 31] }
    ]
  }
] as const satisfies SpritesheetMetadata
```

#### Multiple files

```ts
const goblinSpriteMeta = [
  {
    url: '/textures/sprites/goblin/Attack.png',
    type: 'rowColumn',
    width: 8,
    height: 1,
    animations: [{ name: 'attack', frameRange: [0, 7] }]
  },
  {
    url: '/textures/sprites/goblin/Death.png',
    type: 'rowColumn',
    width: 4,
    height: 1,
    animations: [{ name: 'death', frameRange: [0, 3] }]
  },
  {
    url: '/textures/sprites/goblin/Idle.png',
    type: 'rowColumn',
    width: 4,
    height: 1,
    animations: [{ name: 'idle', frameRange: [0, 3] }]
  }
] as const satisfies SpritesheetMetadata
```

## Static sprites & Atlassing

This component focuses on targetting animated sprites, but it's possible to use it for static images as well. If each frame of the spritesheet is a separate animation, then it effectively acts as an [atlas](https://en.wikipedia.org/wiki/Texture_atlas) with named sprites.

The `<Tree/>` component in the example above does this.
![Tree sprite atlas](/textures/sprites/trees-pixelart.png)

Set `autoUpdate={false}` on static components and only update it manually with `sprite.update()`. This has to be done when the InstancedSprite is initiated or when spritesheet or atlas change.
If you don't do it, then the spritesheet will run animation updates each frame to run animations that don't really exist.
